/*
 * @Author: 0x9DEFA478
 * @Date: 2025-08-27 20:19:36
 * @LastEditTime: 2025-09-14 14:08:36
 * @LastEditors: 0x9DEFA478
 * @Description: 
 * 
 */
#ifndef hMemory_H_Allocator_HPP_
#define hMemory_H_Allocator_HPP_
#include "H_Type.hpp"
#include <cstddef>
#if __cplusplus >= 202000L
#include <concepts>
#endif


namespace H{

#if __cplusplus >= 202000L
  namespace Concept{
    template<typename T>
    concept allocator=requires(usize Size,void* Pointer){
      {T::Alloc(Size)}->std::same_as<void*>;
      T::Free(Pointer);
    };
  }
#endif

  template<typename T>
  struct is_allocator{
    template<typename T_Obj
      ,void* (*)(usize) = &T_Obj::Alloc
      ,void (*)(void*)noexcept = &T_Obj::Free
    >
    static constexpr bool check(T_Obj* obj){return true;}
    static constexpr bool check(...){return false;}

    static constexpr bool value=check(static_cast<T*>(nullptr));
  };
  template<typename T>
  inline constexpr bool is_allocator_v=is_allocator<T>::value;


  class AllocatorDefault{
    public:
      static void* Alloc(usize Size){
        return ::operator new(Size);
      }
      static void Free(void* Pointer)noexcept{
        ::operator delete(Pointer);
      }
  };


  namespace Pool{

#if __cplusplus >= 202000L
    template<Concept::allocator Allocator,usize ObjectSize>
#else
    template<typename Allocator,usize ObjectSize>
#endif
    class Bypass{
#if __cplusplus < 202000L
      static_assert(is_allocator_v<Allocator>);
#endif

      public:
        Bypass()noexcept=default;
        ~Bypass()noexcept=default;

        Bypass(const Bypass&)=delete;
        Bypass(Bypass&& objectInert)noexcept=default;

        Bypass& operator=(const Bypass&)=delete;
        Bypass& operator=(Bypass&& objectInert)noexcept=default;

        void* Alloc(){
          return Allocator::Alloc(ObjectSize);
        }

        void Free(void* Pointer)noexcept{
          Allocator::Free(Pointer);
        }

        void Trim()noexcept{}
        
        static void Dealloc(void* Pointer)noexcept{
          Allocator::Free(Pointer);
        }
    };


#if __cplusplus >= 202000L
    template<Concept::allocator Allocator,usize ObjectSize>
#else
    template<typename Allocator,usize ObjectSize>
#endif
    class ObjectInert{
#if __cplusplus < 202000L
      static_assert(is_allocator_v<Allocator>);
#endif

      private:
        struct Memory{
          Memory* Next;

          static constexpr std::size_t HeaderSize()noexcept{return IncreaseAlignTo(sizeof(Memory),Min(ObjectSize,alignof(std::max_align_t)));}

          static Memory* Cast(void* m)noexcept{return reinterpret_cast<Memory*>(static_cast<byte*>(m)-HeaderSize());}
          static void* ToMemory(Memory* oim)noexcept{return static_cast<void*>(reinterpret_cast<byte*>(oim)+HeaderSize());}

          static Memory* Alloc(usize Size){
            return static_cast<Memory*>(Allocator::Alloc(HeaderSize()+Size));
          }

          static void Free(Memory* oim)noexcept{
            Allocator::Free(static_cast<void*>(oim));
          }
        };

      public:
        ObjectInert()noexcept:objectList(nullptr){}
        ~ObjectInert()noexcept{Trim();}

        ObjectInert(const ObjectInert&)=delete;
        ObjectInert(ObjectInert&& objectInert)noexcept:objectList(objectInert.objectList){
          objectInert.objectList=nullptr;
        }

        ObjectInert& operator=(const ObjectInert&)=delete;
        ObjectInert& operator=(ObjectInert&& objectInert)noexcept{
          if(this==&objectInert){
            return *this;
          }
          Trim();
          objectList=objectInert.objectList;
          objectInert.objectList=nullptr;
          return *this;
        }

        void* Alloc(){
          auto p=static_cast<Memory*>(objectList);
      
          if(p!=nullptr){
            objectList=static_cast<void*>(p->Next);
            return Memory::ToMemory(p);
          }
          return Memory::ToMemory(Memory::Alloc(ObjectSize));
        }

        void Free(void* Pointer)noexcept{
          auto p=Memory::Cast(Pointer);
          p->Next=static_cast<Memory*>(objectList);
          objectList=static_cast<void*>(p);
        }

        void Trim()noexcept{
          auto p=static_cast<Memory*>(objectList);
          while(p!=nullptr){
            auto free_p=p;
            p=p->Next;
            Memory::Free(free_p);
          }
          objectList=nullptr;
        }
        
        static void Dealloc(void* Pointer)noexcept{
          Memory::Free(Memory::Cast(Pointer));
        }

      private:
        void* objectList;
    };

  }

}


#endif //hMemory_H_Allocator_HPP_
